using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using DimensionsHelper.Common.Contracts.Excel;
using DimensionsHelper.Common.Excel;
using MsExcel = Microsoft.Office.Interop.Excel;

namespace DimensionsHelper.ExcelAddIn.Ms.Operator
{

    public class ExcelWorksheetOperator : IExcelWorksheetOperator
    {

        public ExcelWorksheetOperator(IExcelWorkbookOperator workbook, MsExcel.Worksheet worksheet)
        {
            Workbook = workbook;
            WorksheetInternal = worksheet;
        }

        ~ExcelWorksheetOperator()
        {
            if (WorksheetInternal != null)
            {
                Marshal.ReleaseComObject(WorksheetInternal);
            }
        }


        private WeakReference<MsExcel.Window> _window;

        private MsExcel.Window GetWindow()
        {
            if (_window?.TryGetTarget(out var window) == true)
            {
                return window;
            }

            var workbook = (MsExcel.Workbook)WorksheetInternal.Parent;
            foreach (var w in workbook.Windows.OfType<MsExcel.Window>())
            {
                if (w.ActiveSheet != WorksheetInternal)
                {
                    continue;
                }

                _window = new WeakReference<MsExcel.Window>(w);
                return w;
            }

            return null;
        }


        internal MsExcel.Worksheet WorksheetInternal { get; }

        public IExcelWorkbookOperator Workbook { get; }

        public string Name
        {
            get => WorksheetInternal.Name;
            set => WorksheetInternal.Name = value;
        }

        public int StartRow => WorksheetInternal.UsedRange.Row;

        public int StartColumn => WorksheetInternal.UsedRange.Column;

        public int EndRow => WorksheetInternal.UsedRange.Row + WorksheetInternal.UsedRange.Rows.Count;

        public int EndColumn => WorksheetInternal.UsedRange.Column + WorksheetInternal.Columns.Count;

        public ExcelStyleColor TabColor
        {
            get => ((Color)WorksheetInternal.Tab.Color).ToExcelColor();
            set => WorksheetInternal.Tab.Color = value.ToColor();
        }

        public bool ShowGridLines
        {
            get => GetWindow()?.DisplayGridlines == true;
            set
            {
                if (GetWindow() is MsExcel.Window window)
                {
                    window.DisplayGridlines = value;
                }
            }
        }

        public int Zoom
        {
            get => (int?)GetWindow()?.Zoom ?? 100;
            set
            {
                if (GetWindow() is MsExcel.Window window)
                {
                    window.Zoom = value;
                }
            }
        }

        public void AutoFitEntireColumns()
        {
            WorksheetInternal.Columns.EntireColumn.AutoFit();
        }

        public void DeleteRow(int row)
        {
            MsExcel.Range rowRange = null;

            try
            {
                rowRange = (MsExcel.Range)WorksheetInternal.Rows[row];
                rowRange.Delete();
            }
            finally
            {
                if (rowRange != null)
                {
                    Marshal.ReleaseComObject(rowRange);
                }
            }
        }

        public void DeleteColumn(int column)
        {
            MsExcel.Range columnRange = null;

            try
            {
                columnRange = (MsExcel.Range)WorksheetInternal.Columns[column];
                columnRange.Delete();
            }
            finally
            {
                if (columnRange != null)
                {
                    Marshal.ReleaseComObject(columnRange);
                }                
            }
        }

        public string GetCellText(int row, int column)
        {
            MsExcel.Range cell = null;

            try
            {
                cell = (MsExcel.Range)WorksheetInternal.Cells[row, column];
                return cell?.ToString() ?? string.Empty;
            }
            finally
            {
                if (cell != null)
                {
                    Marshal.ReleaseComObject(cell);
                }
            }
        }

        public IExcelRangeOperator GetRange(int row, int column)
        {
            return new ExcelRangeOperator(this, (MsExcel.Range)WorksheetInternal.Cells[row, column]);
        }

        public IExcelRangeOperator GetRange(int fromRow, int fromColumn, int toRow, int toColumn)
        {
            return new ExcelRangeOperator(this,
                WorksheetInternal.Range[
                    WorksheetInternal.Cells[fromRow, fromColumn],
                    WorksheetInternal.Cells[toRow, toColumn]
                ]);
        }

        public IExcelRangeOperator GetRow(int row)
        {
            return new ExcelRangeOperator(this, (MsExcel.Range)WorksheetInternal.Rows[row]);
        }

        public object GetValues() => WorksheetInternal.UsedRange.Value2;

        public void InsertRow(int row, int rows = 1)
        {
            if (row <= 0 || rows <= 0)
            {
                return;
            }

            var sourceRow = (MsExcel.Range)WorksheetInternal.Rows[row];
            while (row > 0)
            {
                sourceRow.Insert();
                row--;
            }
        }

        public void MoveToStart()
        {
            if (Workbook.Worksheets.Count <= 1)
            {
                return;
            }

            var firstSheet = ((ExcelWorkbookOperator)Workbook).WorkbookInternal.Worksheets[1];
            if (firstSheet == WorksheetInternal)
            {
                return;
            }

            WorksheetInternal.Move(Before: firstSheet);
        }

        public void MoveToEnd()
        {
            if (Workbook.Worksheets.Count <= 1)
            {
                return;
            }

            var workbook = ((ExcelWorkbookOperator)Workbook).WorkbookInternal;
            var last = workbook.Worksheets[workbook.Worksheets.Count];

            if (last == WorksheetInternal)
            {
                return;
            }

            WorksheetInternal.Move(After: last);
        }

        public void SetColumnAlignment(int column, ExcelHAlign horizontalAlignment, ExcelVAlign verticalAlignment)
        {
            if (column <= 0)
            {
                return;
            }

            var range = (MsExcel.Range)WorksheetInternal.Columns[column];
            range.HorizontalAlignment = ExcelRangeOperator.OperatorHorizontalAlignmentToExcelAlignment(horizontalAlignment);
            range.VerticalAlignment = ExcelRangeOperator.OperatorVerticalAlignmentToExcelAlignment(verticalAlignment);
        }

        public void SetColumnAlignment(ExcelHAlign horizontalAlignment, ExcelVAlign verticalAlignment)
        {
            var range = WorksheetInternal.UsedRange;

            if (range is null)
            {
                return;
            }

            range.HorizontalAlignment = ExcelRangeOperator.OperatorHorizontalAlignmentToExcelAlignment(horizontalAlignment);
            range.VerticalAlignment = ExcelRangeOperator.OperatorVerticalAlignmentToExcelAlignment(verticalAlignment);
        }

        public void SetColumnWidth(double width)
        {
            WorksheetInternal.Columns.EntireColumn.ColumnWidth = width;
        }

        public void SetColumnWidth(int column, double width)
        {
            if (column <= 0)
            {
                return;
            }

            var range = (MsExcel.Range)WorksheetInternal.Columns[column];
            range.ColumnWidth = width;
        }
    }

}